/*
*
*	The Gimme ECMAScript Library by Stephen Stchur
*	"Taking the pain out of Javascript!"
*
*	Copyright (c) Microsoft Corporation.  All rights reserved.
*
*	Description:
*	Gimme Events Module / Extensions:	Provides a "home" for event-related functionality and defines some event extensions which qualize browser differences among several proprietary events
*
*	Requires:
*	Gimme.query.js
*	Gimme.object.js
*
*/

Gimme.Events = new function()
{
	var evtHash = {};
	var capturingElem = null;
	var rerouteFn = null;
	
	this.captureMouse = function(_elem)
	{
		Gimme.Events.releaseMouse();
		capturingElem = _elem;
		
		if (typeof _elem.setCapture !== 'undefined')
		{
			_elem.setCapture();
		}
		else
		{
			rerouteFn = function(e)
			{
				var synthEvent, scrollPos;
				if (Gimme.Browser.isGecko)
				{
					synthEvent = document.createEvent('MouseEvents');
					synthEvent.initMouseEvent(
						e.type							// type
						,e.bubbles						// can bubble?
						,e.cancelable					// cancelable?
						,window							// always window here
						,e.detail						// mouse click count
						,e.screenX						// event's screen x coordinate (copied from the passed in event)
						,e.screenY						// event's screen y coordinate (copied from the passed in event)
						,e.clientX						// event's client x coordinate (copied from the passed in event)
						,e.clientY						// event's client y coordinate (copied from the passed in event)
						,e.ctrlKey						// whether or not the ctrl key was pressed during the event (copied from the passed in event)
						,e.altKey						// whether or not the alt key was pressed during the event (copied from the passed in event)
						,e.shiftKey						// whether or not the shift key was pressed during the event (copied from the passed in event)
						,e.metaKey						// whether or not the meta key was pressed during the event (copied from the passed in event)
						,e.button						// indicates which button (if any) caused the mouse event (copied from the passed in event)
						,e.relatedTarget				// relatedTarget (only applicable for 'mouseover' and 'mouseout' events)
					);
					
					scrollPos = Gimme.Screen.getScrollPosition();
					synthEvent.__defineGetter__('pageX', function() { return this.clientX + scrollPos.x; });
					synthEvent.__defineGetter__('pageY', function() { return this.clientY + scrollPos.y; });
				}
				else
				{
					synthEvent = e;
				}
				
				document.removeEventListener(e.type, rerouteFn, true);
				synthEvent.captureTarget = e.target;
				_elem.dispatchEvent(synthEvent);
				if (rerouteFn !== null)
				{
					document.addEventListener(e.type, rerouteFn, true);
				}
				delete synthEvent.captureTarget;
				
				e.stopPropagation();
			}
			
			document.addEventListener('mouseover', rerouteFn, true);
			document.addEventListener('mouseout', rerouteFn, true);
			document.addEventListener('mousemove', rerouteFn, true);
			document.addEventListener('mouseup', rerouteFn, true);
			document.addEventListener('mousedown', rerouteFn, true);
			document.addEventListener('click', rerouteFn, true);
			document.addEventListener('dblclick', rerouteFn, true);
		}
		
		return this;
	};
	
	this.releaseMouse = function()
	{
		if (capturingElem !== null)
		{
			if (typeof capturingElem.releaseCapture !== 'undefined')
			{
				capturingElem.releaseCapture();
			}
			else
			{
				document.removeEventListener('mouseover', rerouteFn, true);
				document.removeEventListener('mouseout', rerouteFn, true);
				document.removeEventListener('mousemove', rerouteFn, true);
				document.removeEventListener('mouseup', rerouteFn, true);
				document.removeEventListener('mousedown', rerouteFn, true);
				document.removeEventListener('click', rerouteFn, true);
				document.removeEventListener('dblclick', rerouteFn, true);
			}
			
			capturingElem = rerouteFn = null;
		}
		
		return this;
	};

	this.getCaptureTarget = function(_evt)
	{
		return _evt.captureTarget || _evt.srcElement || _evt.target;
	};
	
	Gimme.ext.onmouseenter = function(_fn, _useCapture, _listening)
	{
		var f = mouseEnter(_fn);
		_listening ? this.addEvent('mouseover', f, _useCapture, false) : this.removeEvent('mouseover', f, _useCapture, false);
		f = null;
	};

	Gimme.ext.onmouseleave = function(_fn, _useCapture, _listening)
	{
		var f = mouseEnter(_fn);
		_listening ? this.addEvent('mouseout', f, _useCapture, false) : this.removeEvent('mouseout', f, _useCapture, false);
		f = null;
	}

	Gimme.ext.onmousewheel = function(_fn, _useCapture, _listening)
	{
		var evtName = 'mousewheel'
		,f = _fn;
		
		if (Gimme.Browser.isGecko)
		{
			evtName = 'DOMMouseScroll';
			f = mouseWheel(_fn);
		}
		
		_listening ? this.addEvent(evtName, f, _useCapture, false) : this.removeEvent(evtName, f, _useCapture, false);
	};
	
	
	// Private helper functions

	function isAnAncestorOf(_ancestor, _descendant, _generation)
	{
		if (_ancestor === _descendant) { return false; }
		
		var gen = 0;
		while (_descendant && _descendant != _ancestor)
		{
			gen++;
			_descendant = _descendant.parentNode;
		}
		
		_generation = _generation || gen;
		return _descendant === _ancestor && _generation === gen;		
	}
	
	function mouseEnter(_fn)
	{	
		var key = Gimme.Helper.getObjectGUID(_fn);
		var f = evtHash[key];
		if (typeof f === 'undefined')
		{
			f = evtHash[key] = function(_evt)
			{
				var relTarget = _evt.relatedTarget;
				if (this === relTarget || isAnAncestorOf(this, relTarget)) { return; }
		
				_fn.call(this, _evt);
			};
		}
		return f;	
	}
	
	function mouseWheel(_fn)
	{
		var key = Gimme.Helper.getObjectGUID(_fn);
		var f = evtHash[key];
		if (typeof f === 'undefined')
		{
			f = evtHash[key] = function(_evt)
			{
				_evt.wheelDelta = -(_evt.detail);
				_fn.call(this, _evt);
				_evt.wheelDelta = null;
			}
		}
		return f;
	}
};

